﻿/*
 * Copyright (c) Martin Kinkelin
 *
 * See the "License.txt" file in the root directory for infos
 * about permitted and prohibited uses of this code.
 */

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;

namespace Jarf
{
	/// <summary>
	/// Allows editing of a backup task.
	/// </summary>
	public partial class BackupTaskDialog : BaseDialog
	{
		private BackupTask _task;
		private List<string> _excludedItems;



		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="task">Backup task to be edited.</param>
		public BackupTaskDialog(BackupTask task)
		{
			if (task == null)
				throw new ArgumentNullException("task");

			_task = task;
			_excludedItems = new List<string>(task.Exclusions);

			InitializeComponent();

			// set up the source
			if (!string.IsNullOrEmpty(_task.Source))
				sourceFolderTextBox.Text = _task.Source;

			// set up the destination
			if (!string.IsNullOrEmpty(_task.Destination))
			{
				if (_task.DestinationIsServer)
				{
					destinationServerRadioButton.Checked = true;

					ParseRsyncUri();
					serverPasswordTextBox.Text = _task.Password;
				}
				else
					destinationFolderTextBox.Text = _task.Destination;
			}

			deleteExtraneousFilesCheckBox.Checked = _task.DeleteExtraneousFiles;
			overwriteNewerFilesCheckBox.Checked = _task.OverwriteNewerFiles;

			// set Changed to false because the invoked event handlers set it to true
			HasChanged = false;
		}

		private void ParseRsyncUri()
		{
			// remove the "rsync://" prefix
			string uri = _task.Destination.Substring(8);

			// search the optional '@'
			int atSeparatorIndex = uri.IndexOf('@');
			// search '/'
			int slashSeparatorIndex = uri.IndexOf('/', (atSeparatorIndex >= 0 ? atSeparatorIndex + 1 : 0));
			// search the optional ':'
			int colonSeparatorIndex = uri.LastIndexOf(':', slashSeparatorIndex - 1);

			// extract the module
			serverModuleTextBox.Text = uri.Substring(slashSeparatorIndex + 1);
			uri = uri.Substring(0, slashSeparatorIndex);

			// extract the optional port
			if (colonSeparatorIndex >= 0)
			{
				serverPortTextBox.Text = int.Parse(uri.Substring(colonSeparatorIndex + 1)).ToString();
				uri = uri.Substring(0, colonSeparatorIndex);
			}

			// extract the optional user and the host
			if (atSeparatorIndex >= 0)
			{
				serverUserTextBox.Text = uri.Substring(0, atSeparatorIndex);
				serverHostTextBox.Text = uri.Substring(atSeparatorIndex + 1);
			}
			else
			{
				serverUserTextBox.Text = string.Empty;
				serverHostTextBox.Text = uri;
			}
		}


		private void sourceFolderTextBox_TextChanged(object sender, EventArgs e)
		{
			excludedSourceItemsButton.Enabled = Directory.Exists(sourceFolderTextBox.Text);
			HasChanged = true;
		}

		private void browseSourceFolderButton_Click(object sender, EventArgs e)
		{
			sourceFolderBrowserDialog.SelectedPath = sourceFolderTextBox.Text;

			if (sourceFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
			{
				sourceFolderTextBox.Text = sourceFolderBrowserDialog.SelectedPath;
				HasChanged = true;
			}
		}

		private void excludedSourceItemsButton_Click(object sender, EventArgs e)
		{
			ExcludedSourceItemsDialog dialog = new ExcludedSourceItemsDialog(
				GetFullPath(sourceFolderTextBox.Text), _excludedItems);

			using (dialog)
			{
				if (dialog.ShowDialog(this) == DialogResult.Yes)
					HasChanged = true;
			}
		}

		private void destinationRadioButton_CheckedChanged(object sender, EventArgs e)
		{
			destinationFolderTextBox.Enabled = destinationFolderRadioButton.Checked;
			browseDestinationFolderButton.Enabled = destinationFolderRadioButton.Checked;

			serverHostLabel.Enabled = serverHostTextBox.Enabled = destinationServerRadioButton.Checked;
			serverPortLabel.Enabled = serverPortTextBox.Enabled = destinationServerRadioButton.Checked;
			serverUserLabel.Enabled = serverUserTextBox.Enabled = destinationServerRadioButton.Checked;
			serverPasswordLabel.Enabled = serverPasswordTextBox.Enabled = destinationServerRadioButton.Checked;
			serverModuleLabel.Enabled = serverModuleTextBox.Enabled = destinationServerRadioButton.Checked;

			HasChanged = true;
		}

		private void browseDestinationFolderButton_Click(object sender, EventArgs e)
		{
			destinationFolderBrowserDialog.SelectedPath = destinationFolderTextBox.Text;

			if (destinationFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
			{
				destinationFolderTextBox.Text = destinationFolderBrowserDialog.SelectedPath;
				HasChanged = true;
			}
		}

		private void Control_Changed(object sender, EventArgs e)
		{
			HasChanged = true;
		}


		private static string GetFullPath(string path)
		{
			string fullPath = Path.GetFullPath(path);
			if (fullPath[fullPath.Length - 1] == Path.DirectorySeparatorChar)
				fullPath = fullPath.Remove(fullPath.Length - 1);

			return fullPath;
		}


		protected override bool ApplyChanges()
		{
			// check if the changes are valid

			string source = sourceFolderTextBox.Text;
			if (!Directory.Exists(source))
			{
				MessageBox.Show("The source folder does not exist.", "Invalid source folder",
					MessageBoxButtons.OK, MessageBoxIcon.Error);
				sourceFolderTextBox.Focus();
				return false;
			}

			source = GetFullPath(source);

			string destination;
			if (destinationFolderRadioButton.Checked)
			{
				// the destination is a folder

				destination = destinationFolderTextBox.Text;
				if (!Directory.Exists(destination))
				{
					MessageBox.Show("The destination folder does not exist.",
						"Invalid destination folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
					destinationFolderTextBox.Focus();
					return false;
				}

				destination = GetFullPath(destination);

				if (destination.Length >= source.Length &&
				    string.Compare(destination + Path.DirectorySeparatorChar, 0,
							source + Path.DirectorySeparatorChar, 0, source.Length + 1, true) == 0)
				{
					MessageBox.Show("The destination folder cannot be in the source folder.",
						"Invalid destination folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
					destinationFolderTextBox.Focus();
					return false;
				}
			}
			else
			{
				// the destination is a server

				if (string.IsNullOrEmpty(serverHostTextBox.Text) ||
						serverHostTextBox.Text.IndexOfAny(new char[] { '@', '/', ' ' }) >= 0)
				{
					MessageBox.Show("The server host is invalid.\nPlease specify a hostname or an IP address.",
						"Invalid server host", MessageBoxButtons.OK, MessageBoxIcon.Error);
					serverHostTextBox.Focus();
					return false;
				}

				ushort port = 0;
				if (!string.IsNullOrEmpty(serverPortTextBox.Text) &&
						(!ushort.TryParse(serverPortTextBox.Text, out port) ||
						 port == 0 || port.ToString() != serverPortTextBox.Text))
				{
					MessageBox.Show("The server port is invalid.\nPlease specify a value from 1 to 65535.",
						"Invalid server port", MessageBoxButtons.OK, MessageBoxIcon.Error);
					serverPortTextBox.Focus();
					return false;
				}

				if (!string.IsNullOrEmpty(serverUserTextBox.Text) && serverUserTextBox.Text.IndexOf('@') >= 0)
				{
					MessageBox.Show("The server user contains an invalid '@' character.",
						"Invalid server user", MessageBoxButtons.OK, MessageBoxIcon.Error);
					serverUserTextBox.Focus();
					return false;
				}

				if (string.IsNullOrEmpty(serverModuleTextBox.Text))
				{
					MessageBox.Show("Please specify a server module.",
						"Invalid server module", MessageBoxButtons.OK, MessageBoxIcon.Error);
					serverModuleTextBox.Focus();
					return false;
				}

				destination = string.Format("rsync://{0}{1}{2}/{3}",
					(string.IsNullOrEmpty(serverUserTextBox.Text) ? string.Empty : serverUserTextBox.Text + '@'),
					serverHostTextBox.Text,
					(port == 0 ? string.Empty : ':' + port.ToString()),
					serverModuleTextBox.Text);
			}

			bool isWindows = (Path.DirectorySeparatorChar == '\\');

			// apply the changes

			// reset the last backup timestamp if source or destination have changed
			if (string.Compare(_task.Source, source, isWindows) != 0 ||
			    string.Compare(_task.Destination, destination, isWindows) != 0)
				_task.LastBackup = null;

			_task.Source = source;
			_task.Destination = destination;

			_task.Password = (destinationServerRadioButton.Checked && !string.IsNullOrEmpty(serverPasswordTextBox.Text) ?
				serverPasswordTextBox.Text : null);

			_task.DeleteExtraneousFiles = deleteExtraneousFilesCheckBox.Checked;
			_task.OverwriteNewerFiles = overwriteNewerFilesCheckBox.Checked;

			_task.Exclusions.Clear();
			_task.Exclusions.AddRange(_excludedItems);

			return true;
		}
	}
}
